/*! 
 * Header file for a Magnus force generator.
 *
 * \author Devis Rossini
 * \date 06 April 2012
 */ 
#ifndef PHXMAGNUSFORCEGENERATOR_H_INCLUDE_GUARD
#define PHXMAGNUSFORCEGENERATOR_H_INCLUDE_GUARD

#include "PhxCore.h"
#include "PhxForceGenerator.h"

#define VELOCITY_MAGNITUDE_THRESHOLD 0.1f

namespace phx
{	
	/*!
     * A force generator that applies a Magnus force to a rigid body moving into a fluid. 
	 *
	 * \note This force generator can only be applied to a spehere.
     */
	class SphereMagnus : public ForceGenerator
	{
	public:

		/*! Creates the generator with the given parameters. */
		SphereMagnus(const real iFluidDensity, const real iSphereRadius)
			:	mFluidDensity(iFluidDensity), 
				mSphereRadius(iSphereRadius) { }

		/*! Applies the spring force to the given rigid body. */
		virtual void UpdateForce(RigidBody* oBody, real iDuration)
		{
			// We'll implement this formula: Fm = 1/2 * Cl * rho * A * v^2
			Vector3 velocity = oBody->GetVelocity();
			Vector3 angularVelocity = oBody->GetAngularVelocity();

			real velocityMagnitude = velocity.Magnitude();
			real angularVelocityMagnitude = angularVelocity.Magnitude();

			if (velocityMagnitude > VELOCITY_MAGNITUDE_THRESHOLD &&
				angularVelocityMagnitude > VELOCITY_MAGNITUDE_THRESHOLD)
			{
				real liftCoefficient =  -0.05f + 
					real_sqrt(0.0025f + 0.36f * mSphereRadius * angularVelocityMagnitude / velocityMagnitude);
	
				real area = PI * mSphereRadius * mSphereRadius;

				real magnusForce = 0.5f * mFluidDensity * liftCoefficient * area * velocityMagnitude * velocityMagnitude; 

				Vector3 force = velocity.CrossProduct(angularVelocity);
				force = force.Versor() * magnusForce;

				//if (force.Magnitude() < 0.1f)
				//	return;

				oBody->AddForce(force);
			}
		}

	private:

		/*! Holds the fluid density. */
		real mFluidDensity;
		/*! Holds the sphere radius. */
		real mSphereRadius;
	};
}

#endif // PHXMAGNUSFORCEGENERATOR_H_INCLUDE_GUARD